并发编程是指在一台处理器上“同时”处理多个任务。

多进程就是用来实现并发编程

多进程可以理解为多个异步的代码

进程就是为了实现代码的异步执行

如果开启了多进程,就是多个进程几乎同时开始执行

进程的使用场景:
  • 如果两个任务需要数据隔离,且想实现异步,那么就是用多进程
  • 高计算类型的程序(只是做算数用的程序)一般都会多进程

开多进程,进程数超过3个以上就使用进程池来处理


multiprocess 模块 -> 综合的管理进程的包

在window下做开发所有创建进程的程序都要放在 if __name__ == '__main__' 下执行,因为当创建一个子进程会把代码在执行一遍,就是为了避免死循环

当多个进程去修改同一个数据的时候,就会引发数据安全或顺序混乱问题。

1. 获取进程的 pid

import os

print(os.getpid())  # 248 获取当前进程的 pid
print(os.getppid())  # 5604 获取当前父进程的 pid

2. Process 类 -> 用于创建进程

  • Process(target=函数名, args=函数接收的参数) -> args 必须接收一个元组

from multiprocessing import Process

def fn(d1, d2):
    print('----子进程----')
    print(d1, d2)

if __name__ == '__main__':

    p = Process(target=fn, args=('数据一', '数据二'))  # 创建一个进程执行一个函数 -> 此时 if __name__ == '__main__': 下的代码就相当于主进程,而 fn函数 就相当于子进程

    p.start()  # 启动进程,执行进程所绑定的函数

    print('-------主进程-------')  # 当进程启动后 p.start() 下方的代码不会等待进程所绑定的函数执行完再开始执行,而是直接往下执行,因为此时进程所绑定的函数已经变成了异步代码了

 # 执行结果:
        # -------主进程-------
        # ----子进程----
        # 数据一 数据二


  • .join() -> 主进程的代码会在 .json() 这里进行阻塞,等待子进程(进程所绑定的函数)执行完后,才会往下执行 -> 当使用了.join(),程序就会变成同步执行了

from multiprocessing import Process

def fn(d1, d2):
    print('----子进程----')
    print(d1, d2)

if __name__ == '__main__':
    p = Process(target=fn, args=('数据一', '数据二'))

    p.start()

    p.join()  # 阻塞 -> 等待子进程(进程所绑定的函数)执行完后,才会往下执行

    print('-------主进程-------')

# 执行结果:
        # ----子进程----
        # 数据一 数据二
        # -------主进程-------

  • 开启多个子进程

# 开启多个子进程一

from multiprocessing import Process

def fn(d1, d2):
    print(d1, d2)

if __name__ == '__main__':
    p1 = Process(target=fn, args=('数据一', '数据二'))  # 创建第一个进程
    p2 = Process(target=fn, args=('data1', 'data2'))  # 创建第二个进程
    p1.start()  # 执行第一个进程
    p2.start()  # 执行第二个进程

    print('-------主进程-------')

 # 执行结果:
        # -------主进程-------
        # 数据一 数据二
        # data1 data2

# 开启多个子进程二

from multiprocessing import Process

def fn(data):
    print(data)

if __name__ == '__main__':
    for i in range(10):
        p = Process(target=fn, args=(i,))
        p.start()

    print('-------主进程-------')

# 执行结果:
        # 0
        # 1
        # 2
        # 3
        # 4
        # -------主进程-------
        # 5
        # 6
        # 7
        # 8
        # 9

  • 当所有子进程执行完后,主进程才开始执行

# 正确示范一

from multiprocessing import Process

def fn(d1, d2):
    print(d1, d2)

if __name__ == '__main__':
    p1 = Process(target=fn, args=('数据一', '数据二'))
    p2 = Process(target=fn, args=('data1', 'data2'))
    p1.start()
    p2.start()

    p1.join()
    p2.join()

    print('-------主进程-------')

 # 执行结果:
        # 数据一 数据二
        # data1 data2
        # -------主进程-------

# 正确示范二

from multiprocessing import Process

def fn(data):
    print(data)

if __name__ == '__main__':
    p_list = []
    for i in range(10):
        p = Process(target=fn, args=(i,))
        p.start()
        p_list.append(p)  # 将所有子进程放进列表中

    for i in p_list:  # 当所有子进程都执行完了才会继续往执行
        i.join()

    print('-------主进程-------')

# 执行结果:
        # 1
        # 0
        # 2
        # 3
        # 4
        # 5
        # 6
        # 7
        # 8
        # 9
        # -------主进程-------

# 错误示范一

from multiprocessing import Process

def fn(data):
    print(data)

if __name__ == '__main__':
    p_list = []
    for i in range(10):
        p = Process(target=fn, args=(i,))
        p.start()
        p.join()  # 如果join放在循环里就表示当绑定的函数执行完才循环一次

    print('-------主进程-------')

# 错误示范二

from multiprocessing import Process

def fn(data):
    print(data)

if __name__ == '__main__':
    p_list = []
    for i in range(10):
        p = Process(target=fn, args=(i,))
        p.start()

    p.join()  # 如果 join放在外面就表示阻塞子进程最后一个

    print('-------主进程-------')

  • 开启多个子进程使用 .join() 的注意事项

from multiprocessing import Process

def fn(d1, d2):
    print('----子进程----')
    print(d1, d2)

if __name__ == '__main__':
    p1 = Process(target=fn, args=('数据一', '数据二'))
    p2 = Process(target=fn, args=('data1', 'data2'))
    p1.start()
    p2.start()

    p1.join()  # 此时等待p1的子进程(进程所绑定的函数)执行完后,才会往下执行,而不是等待所有子进程结束

    print('-------主进程-------')

 # 执行结果:
        # ----子进程----
        # 数据一 数据二
        # -------主进程-------
        # ----子进程----
        # data1 data2


2.进程函数调用外部变量或函数的注意事项

  • 如果要调用的变量和函数在 if __name__ == '__main__': 里面必须用传参的形式调用不然就会报错,因为进程函数无法直接调用 if __name__ == '__main__': 里面的方法和函数,如果是直接执行该函数是可以的,但是如果是被进程执行就不行

from multiprocessing import Process


def fun(if_data):
    print(data)  # 直接调用进程函数外部的并且不在 if __name__ == '__main__': 里面的变量,函数是可以直接调用函数外部的变量或方法(因为作用域链)
    print(if_data)  # 使用传参的形式调用 if __name__ == '__main__': 里面的变量


data = '外部参数'
if __name__ == '__main__':
    if_data = 'if_main里面的参数'
    p = Process(target=fun, args=(if_data,))
    p.start()

3. 创建进程的方法二 -> 通过继承方式

import os
from multiprocessing import Process


class MyProcess(Process):  # 通过继承 Process 类,从而创建一个进程
    def __init__(self, d1, d2):
        super().__init__()
        self.d1 = d1
        self.d2 = d2

    def run(self):  # run 方法就相当于进程所绑定的函数
        print('子进程: ', os.getpid(), self.d1, self.d2)
        self.other_fn()

    def other_fn(self):
        print('其他方法')


if __name__ == '__main__':
    p = MyProcess('数据1', '数据2')  # 创建一个进程
    p.start()  # 当进程启动的时候就会调用类中的 run 方法
    print('-----主进程-----')

# 执行结果:
        # -----主进程-----
        # 子进程:  3116 数据1 数据2
        # 其他方法

4. 多进程的应用

  • 创建多个进程实现TCP协议多人聊天 -> 因为每个进程之间是独立的所以可以实现TCP协议的多人通讯

# server.py

import socket
from multiprocessing import Process

def talk(conn):
    conn.send(b'world')
    msg = conn.recv(1024).decode('utf-8')
    print(msg)

if __name__ == '__main__':
    sk = socket.socket()
    sk.bind(('127.0.0.1', 8080))
    sk.listen()
    while True:
        conn, addr = sk.accept()  # 等待客户端链接
        p = Process(target=talk, args=(conn,))
        p.start()
    conn.close()
    sk.close()

# client1.py

import socket

sk = socket.socket()
sk.connect(('127.0.0.1', 8080))

print(sk.recv(1024).decode('utf-8'))
sk.send(input('>>>').encode('utf-8'))
sk.close()

# client2.py

import socket

sk = socket.socket()
sk.connect(('127.0.0.1', 8080))

print(sk.recv(1024).decode('utf-8'))
sk.send(input('>>>').encode('utf-8'))
sk.close()

5. 守护进程

  • 守护进程会随着主进程的代码执行结束而结束,不会等待其他子进程


# 报时器例子: 每个1秒就会报一次时
# 开启了守护进程 -> cal_time 会随着主进程的代码执行结束而结束

import time
from multiprocessing import Process

def cal_time():
    while True:
        time.sleep(1)
        print('过去了1秒')

if __name__ == '__main__':
    p = Process(target=cal_time)
    p.daemon = True  # 开启守护进程,一定要设置在 start 之前
    p.start()
    for i in range(100):
        time.sleep(0.1)
        print('*' * i)

# 报时器例子: 每个1秒就会报一次时
# 没有开启守护进程 -> cal_time子进程会一直执行

import time
from multiprocessing import Process

def cal_time():
    while True:
        time.sleep(1)
        print('过去了1秒')

if __name__ == '__main__':
    p = Process(target=cal_time)
    p.start()
    for i in range(100):
        time.sleep(0.1)
        print('*' * i)

# 上面图片的代码

import time
from multiprocessing import Process

# 守护进程
def cal_time():
    while True:
        time.sleep(1)
        print('过去了1秒')

# 其他子进程
def fun():
    print('--' * 10)
    time.sleep(15)
    print('--' * 10)

if __name__ == '__main__':
 # 守护进程
    p = Process(target=cal_time)
    p.daemon = True  # 开启守护进程
    p.start()


 # 其他子进程
    p2 = Process(target=fun)
    p2.start()


# 主进程代码
    for i in range(100):
        time.sleep(0.1)
        print('*' * i)
    p2.join()  # 如果守护进程想等待其他进程结束后再结束,可以使用join

6.进程的其他方法

  • .is_alive() -> 判断进程是否存活 -> 返回值: True: 进程没有结束 False 进程以结束

import time
from multiprocessing import Process

def fun():
    print('---子进程---')

if __name__ == '__main__':
    p = Process(target=fun)
    p.start()
    print(p.is_alive())  # True
    time.sleep(5)
    print(p.is_alive())  # False

  • .terminate() -> 手动结束进程 -> 如果调用了 terminate 进程是不会立刻结束的,terminate只是向系统发送了一条结束该进程的指令,terminate就相当于异步执行

import time
from multiprocessing import Process

def fun():
    print('---子进程---')

if __name__ == '__main__':
    p = Process(target=fun)
    p.start()
    print(p.is_alive())  # True
    p.terminate()
    print(p.is_alive())  # True -> 进程是不会立刻结束的,terminate只是向系统发送了一条结束该进程的指令,terminate就相当于异步执行
    time.sleep(0.1)
    print(p.is_alive())  # False